home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-9.10-netbook-remix-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / louie / dispatcher.py < prev    next >
Text File  |  2005-12-19  |  20KB  |  588 lines

  1. """Multiple-producer-multiple-consumer signal-dispatching.
  2.  
  3. ``dispatcher`` is the core of Louie, providing the primary API and the
  4. core logic for the system.
  5.  
  6. Internal attributes:
  7.  
  8. - ``WEAKREF_TYPES``: Tuple of types/classes which represent weak
  9.   references to receivers, and thus must be dereferenced on retrieval
  10.   to retrieve the callable object
  11.         
  12. - ``connections``::
  13.  
  14.     { senderkey (id) : { signal : [receivers...] } }
  15.     
  16. - ``senders``: Used for cleaning up sender references on sender
  17.   deletion::
  18.  
  19.     { senderkey (id) : weakref(sender) }
  20.     
  21. - ``senders_back``: Used for cleaning up receiver references on receiver
  22.   deletion::
  23.  
  24.     { receiverkey (id) : [senderkey (id)...] }
  25. """
  26.  
  27. import os
  28. import weakref
  29.  
  30. try:
  31.     set
  32. except NameError:
  33.     from sets import Set as set, ImmutableSet as frozenset
  34.  
  35. from louie import error
  36. from louie import robustapply
  37. from louie import saferef
  38. from louie.sender import Any, Anonymous
  39. from louie.signal import All
  40.  
  41.  
  42. # Support for statistics.
  43. if __debug__:
  44.     connects = 0
  45.     disconnects = 0
  46.     sends = 0
  47.  
  48.     def print_stats():
  49.         print ('\n'
  50.                'Louie connects: %i\n'
  51.                'Louie disconnects: %i\n'
  52.                'Louie sends: %i\n'
  53.                '\n') % (connects, disconnects, sends)
  54.  
  55.     if 'PYDISPATCH_STATS' in os.environ:
  56.         import atexit
  57.         atexit.register(print_stats)
  58.  
  59.  
  60.  
  61. WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref)
  62.  
  63.  
  64. connections = {}
  65. senders = {}
  66. senders_back = {}
  67. plugins = []
  68.  
  69. def reset():
  70.     """Reset the state of Louie.
  71.  
  72.     Useful during unit testing.  Should be avoided otherwise.
  73.     """
  74.     global connections, senders, senders_back, plugins
  75.     connections = {}
  76.     senders = {}
  77.     senders_back = {}
  78.     plugins = []
  79.  
  80.  
  81. def connect(receiver, signal=All, sender=Any, weak=True):
  82.     """Connect ``receiver`` to ``sender`` for ``signal``.
  83.  
  84.     - ``receiver``: A callable Python object which is to receive
  85.       messages/signals/events.  Receivers must be hashable objects.
  86.  
  87.       If weak is ``True``, then receiver must be weak-referencable (more
  88.       precisely ``saferef.safe_ref()`` must be able to create a
  89.       reference to the receiver).
  90.     
  91.       Receivers are fairly flexible in their specification, as the
  92.       machinery in the ``robustapply`` module takes care of most of the
  93.       details regarding figuring out appropriate subsets of the sent
  94.       arguments to apply to a given receiver.
  95.  
  96.       Note: If ``receiver`` is itself a weak reference (a callable), it
  97.       will be de-referenced by the system's machinery, so *generally*
  98.       weak references are not suitable as receivers, though some use
  99.       might be found for the facility whereby a higher-level library
  100.       passes in pre-weakrefed receiver references.
  101.  
  102.     - ``signal``: The signal to which the receiver should respond.
  103.     
  104.       If ``All``, receiver will receive all signals from the indicated
  105.       sender (which might also be ``All``, but is not necessarily
  106.       ``All``).
  107.         
  108.       Otherwise must be a hashable Python object other than ``None``
  109.       (``DispatcherError`` raised on ``None``).
  110.         
  111.     - ``sender``: The sender to which the receiver should respond.
  112.     
  113.       If ``Any``, receiver will receive the indicated signals from any
  114.       sender.
  115.         
  116.       If ``Anonymous``, receiver will only receive indicated signals
  117.       from ``send``/``send_exact`` which do not specify a sender, or
  118.       specify ``Anonymous`` explicitly as the sender.
  119.  
  120.       Otherwise can be any python object.
  121.         
  122.     - ``weak``: Whether to use weak references to the receiver.
  123.       
  124.       By default, the module will attempt to use weak references to
  125.       the receiver objects.  If this parameter is ``False``, then strong
  126.       references will be used.
  127.  
  128.     Returns ``None``, may raise ``DispatcherTypeError``.
  129.     """
  130.     if signal is None:
  131.         raise error.DispatcherTypeError(
  132.             'Signal cannot be None (receiver=%r sender=%r)'
  133.             % (receiver, sender))
  134.     if weak:
  135.         receiver = saferef.safe_ref(receiver, on_delete=_remove_receiver)
  136.     senderkey = id(sender)
  137.     if connections.has_key(senderkey):
  138.         signals = connections[senderkey]
  139.     else:
  140.         connections[senderkey] = signals = {}
  141.     # Keep track of senders for cleanup.
  142.     # Is Anonymous something we want to clean up?
  143.     if sender not in (None, Anonymous, Any):
  144.         def remove(object, senderkey=senderkey):
  145.             _remove_sender(senderkey=senderkey)
  146.         # Skip objects that can not be weakly referenced, which means
  147.         # they won't be automatically cleaned up, but that's too bad.
  148.         try:
  149.             weak_sender = weakref.ref(sender, remove)
  150.             senders[senderkey] = weak_sender
  151.         except:
  152.             pass
  153.     receiver_id = id(receiver)
  154.     # get current set, remove any current references to
  155.     # this receiver in the set, including back-references
  156.     if signals.has_key(signal):
  157.         receivers = signals[signal]
  158.         _remove_old_back_refs(senderkey, signal, receiver, receivers)
  159.     else:
  160.         receivers = signals[signal] = []
  161.     try:
  162.         current = senders_back.get(receiver_id)
  163.         if current is None:
  164.             senders_back[receiver_id] = current = []
  165.         if senderkey not in current:
  166.             current.append(senderkey)
  167.     except:
  168.         pass
  169.     receivers.append(receiver)
  170.     # Update stats.
  171.     if __debug__:
  172.         global connects
  173.         connects += 1
  174.  
  175.  
  176. def disconnect(receiver, signal=All, sender=Any, weak=True):
  177.     """Disconnect ``receiver`` from ``sender`` for ``signal``.
  178.  
  179.     - ``receiver``: The registered receiver to disconnect.
  180.     
  181.     - ``signal``: The registered signal to disconnect.
  182.     
  183.     - ``sender``: The registered sender to disconnect.
  184.     
  185.     - ``weak``: The weakref state to disconnect.
  186.  
  187.     ``disconnect`` reverses the process of ``connect``, the semantics for
  188.     the individual elements are logically equivalent to a tuple of
  189.     ``(receiver, signal, sender, weak)`` used as a key to be deleted
  190.     from the internal routing tables.  (The actual process is slightly
  191.     more complex but the semantics are basically the same).
  192.  
  193.     Note: Using ``disconnect`` is not required to cleanup routing when
  194.     an object is deleted; the framework will remove routes for deleted
  195.     objects automatically.  It's only necessary to disconnect if you
  196.     want to stop routing to a live object.
  197.         
  198.     Returns ``None``, may raise ``DispatcherTypeError`` or
  199.     ``DispatcherKeyError``.
  200.     """
  201.     if signal is None:
  202.         raise error.DispatcherTypeError(
  203.             'Signal cannot be None (receiver=%r sender=%r)'
  204.             % (receiver, sender))
  205.     if weak:
  206.         receiver = saferef.safe_ref(receiver)
  207.     senderkey = id(sender)
  208.     try:
  209.         signals = connections[senderkey]
  210.         receivers = signals[signal]
  211.     except KeyError:
  212.         raise error.DispatcherKeyError(
  213.             'No receivers found for signal %r from sender %r' 
  214.             % (signal, sender)
  215.             )
  216.     try:
  217.         # also removes from receivers
  218.         _remove_old_back_refs(senderkey, signal, receiver, receivers)
  219.     except ValueError:
  220.         raise error.DispatcherKeyError(
  221.             'No connection to receiver %s for signal %s from sender %s'
  222.             % (receiver, signal, sender)
  223.             )
  224.     _cleanup_connections(senderkey, signal)
  225.     # Update stats.
  226.     if __debug__:
  227.         global disconnects
  228.         disconnects += 1
  229.  
  230.  
  231. def get_receivers(sender=Any, signal=All):
  232.     """Get list of receivers from global tables.
  233.  
  234.     This function allows you to retrieve the raw list of receivers
  235.     from the connections table for the given sender and signal pair.
  236.  
  237.     Note: There is no guarantee that this is the actual list stored in
  238.     the connections table, so the value should be treated as a simple
  239.     iterable/truth value rather than, for instance a list to which you
  240.     might append new records.
  241.  
  242.     Normally you would use ``live_receivers(get_receivers(...))`` to
  243.     retrieve the actual receiver objects as an iterable object.
  244.     """
  245.     try:
  246.         return connections[id(sender)][signal]
  247.     except KeyError:
  248.         return []
  249.  
  250.  
  251. def live_receivers(receivers):
  252.     """Filter sequence of receivers to get resolved, live receivers.
  253.  
  254.     This is a generator which will iterate over the passed sequence,
  255.     checking for weak references and resolving them, then returning
  256.     all live receivers.
  257.     """
  258.     for receiver in receivers:
  259.         if isinstance(receiver, WEAKREF_TYPES):
  260.             # Dereference the weak reference.
  261.             receiver = receiver()
  262.         if receiver is not None:
  263.             # Check installed plugins to make sure this receiver is
  264.             # live.
  265.             live = True
  266.             for plugin in plugins:
  267.                 if not plugin.is_live(receiver):
  268.                     live = False
  269.                     break
  270.             if live:
  271.                 yield receiver
  272.             
  273.  
  274. def get_all_receivers(sender=Any, signal=All):
  275.     """Get list of all receivers from global tables.
  276.  
  277.     This gets all receivers which should receive the given signal from
  278.     sender, each receiver should be produced only once by the
  279.     resulting generator.
  280.     """
  281.     yielded = set()
  282.     for receivers in (
  283.         # Get receivers that receive *this* signal from *this* sender.
  284.         get_receivers(sender, signal),
  285.         # Add receivers that receive *all* signals from *this* sender.
  286.         get_receivers(sender, All),
  287.         # Add receivers that receive *this* signal from *any* sender.
  288.         get_receivers(Any, signal),
  289.         # Add receivers that receive *all* signals from *any* sender.
  290.         get_receivers(Any, All),
  291.         ):
  292.         for receiver in receivers:
  293.             if receiver: # filter out dead instance-method weakrefs
  294.                 try:
  295.                     if not receiver in yielded:
  296.                         yielded.add(receiver)
  297.                         yield receiver
  298.                 except TypeError:
  299.                     # dead weakrefs raise TypeError on hash...
  300.                     pass
  301.  
  302.  
  303. def send(signal=All, sender=Anonymous, *arguments, **named):
  304.     """Send ``signal`` from ``sender`` to all connected receivers.
  305.     
  306.     - ``signal``: (Hashable) signal value; see ``connect`` for details.
  307.  
  308.     - ``sender``: The sender of the signal.
  309.     
  310.       If ``Any``, only receivers registered for ``Any`` will receive the
  311.       message.
  312.  
  313.       If ``Anonymous``, only receivers registered to receive messages
  314.       from ``Anonymous`` or ``Any`` will receive the message.
  315.  
  316.       Otherwise can be any Python object (normally one registered with
  317.       a connect if you actually want something to occur).
  318.  
  319.     - ``arguments``: Positional arguments which will be passed to *all*
  320.       receivers. Note that this may raise ``TypeError`` if the receivers
  321.       do not allow the particular arguments.  Note also that arguments
  322.       are applied before named arguments, so they should be used with
  323.       care.
  324.  
  325.     - ``named``: Named arguments which will be filtered according to the
  326.       parameters of the receivers to only provide those acceptable to
  327.       the receiver.
  328.  
  329.     Return a list of tuple pairs ``[(receiver, response), ...]``
  330.  
  331.     If any receiver raises an error, the error propagates back through
  332.     send, terminating the dispatch loop, so it is quite possible to
  333.     not have all receivers called if a raises an error.
  334.     """
  335.     # Call each receiver with whatever arguments it can accept.
  336.     # Return a list of tuple pairs [(receiver, response), ... ].
  337.     responses = []
  338.     for receiver in live_receivers(get_all_receivers(sender, signal)):
  339.         # Wrap receiver using installed plugins.
  340.         original = receiver
  341.         for plugin in plugins:
  342.             receiver = plugin.wrap_receiver(receiver)
  343.         response = robustapply.robust_apply(
  344.             receiver, original,
  345.             signal=signal,
  346.             sender=sender,
  347.             *arguments,
  348.             **named
  349.             )
  350.         responses.append((receiver, response))
  351.     # Update stats.
  352.     if __debug__:
  353.         global sends
  354.         sends += 1
  355.     return responses
  356.  
  357.  
  358. def send_minimal(signal=All, sender=Anonymous, *arguments, **named):
  359.     """Like ``send``, but does not attach ``signal`` and ``sender``
  360.     arguments to the call to the receiver."""
  361.     # Call each receiver with whatever arguments it can accept.
  362.     # Return a list of tuple pairs [(receiver, response), ... ].
  363.     responses = []
  364.     for receiver in live_receivers(get_all_receivers(sender, signal)):
  365.         # Wrap receiver using installed plugins.
  366.         original = receiver
  367.         for plugin in plugins:
  368.             receiver = plugin.wrap_receiver(receiver)
  369.         response = robustapply.robust_apply(
  370.             receiver, original,
  371.             *arguments,
  372.             **named
  373.             )
  374.         responses.append((receiver, response))
  375.     # Update stats.
  376.     if __debug__:
  377.         global sends
  378.         sends += 1
  379.     return responses
  380.  
  381.  
  382. def send_exact(signal=All, sender=Anonymous, *arguments, **named):
  383.     """Send ``signal`` only to receivers registered for exact message.
  384.  
  385.     ``send_exact`` allows for avoiding ``Any``/``Anonymous`` registered
  386.     handlers, sending only to those receivers explicitly registered
  387.     for a particular signal on a particular sender.
  388.     """
  389.     responses = []
  390.     for receiver in live_receivers(get_receivers(sender, signal)):
  391.         # Wrap receiver using installed plugins.
  392.         original = receiver
  393.         for plugin in plugins:
  394.             receiver = plugin.wrap_receiver(receiver)
  395.         response = robustapply.robust_apply(
  396.             receiver, original,
  397.             signal=signal,
  398.             sender=sender,
  399.             *arguments,
  400.             **named
  401.             )
  402.         responses.append((receiver, response))
  403.     return responses
  404.     
  405.  
  406. def send_robust(signal=All, sender=Anonymous, *arguments, **named):
  407.     """Send ``signal`` from ``sender`` to all connected receivers catching
  408.     errors
  409.  
  410.     - ``signal``: (Hashable) signal value, see connect for details
  411.  
  412.     - ``sender``: The sender of the signal.
  413.     
  414.       If ``Any``, only receivers registered for ``Any`` will receive the
  415.       message.
  416.  
  417.       If ``Anonymous``, only receivers registered to receive messages
  418.       from ``Anonymous`` or ``Any`` will receive the message.
  419.  
  420.       Otherwise can be any Python object (normally one registered with
  421.       a connect if you actually want something to occur).
  422.  
  423.     - ``arguments``: Positional arguments which will be passed to *all*
  424.       receivers. Note that this may raise ``TypeError`` if the receivers
  425.       do not allow the particular arguments.  Note also that arguments
  426.       are applied before named arguments, so they should be used with
  427.       care.
  428.  
  429.     - ``named``: Named arguments which will be filtered according to the
  430.       parameters of the receivers to only provide those acceptable to
  431.       the receiver.
  432.  
  433.     Return a list of tuple pairs ``[(receiver, response), ... ]``
  434.  
  435.     If any receiver raises an error (specifically, any subclass of
  436.     ``Exception``), the error instance is returned as the result for
  437.     that receiver.
  438.     """
  439.     # Call each receiver with whatever arguments it can accept.
  440.     # Return a list of tuple pairs [(receiver, response), ... ].
  441.     responses = []
  442.     for receiver in live_receivers(get_all_receivers(sender, signal)):
  443.         original = receiver
  444.         for plugin in plugins:
  445.             receiver = plugin.wrap_receiver(receiver)
  446.         try:
  447.             response = robustapply.robust_apply(
  448.                 receiver, original,
  449.                 signal=signal,
  450.                 sender=sender,
  451.                 *arguments,
  452.                 **named
  453.                 )
  454.         except Exception, err:
  455.             responses.append((receiver, err))
  456.         else:
  457.             responses.append((receiver, response))
  458.     return responses
  459.  
  460.  
  461. def _remove_receiver(receiver):
  462.     """Remove ``receiver`` from connections."""
  463.     if not senders_back:
  464.         # During module cleanup the mapping will be replaced with None.
  465.         return False
  466.     backKey = id(receiver)
  467.     for senderkey in senders_back.get(backKey, ()):
  468.         try:
  469.             signals = connections[senderkey].keys()
  470.         except KeyError:
  471.             pass
  472.         else:
  473.             for signal in signals:
  474.                 try:
  475.                     receivers = connections[senderkey][signal]
  476.                 except KeyError:
  477.                     pass
  478.                 else:
  479.                     try:
  480.                         receivers.remove(receiver)
  481.                     except Exception:
  482.                         pass
  483.                 _cleanup_connections(senderkey, signal)
  484.     try:
  485.         del senders_back[backKey]
  486.     except KeyError:
  487.         pass
  488.  
  489.             
  490. def _cleanup_connections(senderkey, signal):
  491.     """Delete empty signals for ``senderkey``. Delete ``senderkey`` if
  492.     empty."""
  493.     try:
  494.         receivers = connections[senderkey][signal]
  495.     except:
  496.         pass
  497.     else:
  498.         if not receivers:
  499.             # No more connected receivers. Therefore, remove the signal.
  500.             try:
  501.                 signals = connections[senderkey]
  502.             except KeyError:
  503.                 pass
  504.             else:
  505.                 del signals[signal]
  506.                 if not signals:
  507.                     # No more signal connections. Therefore, remove the sender.
  508.                     _remove_sender(senderkey)
  509.  
  510.  
  511. def _remove_sender(senderkey):
  512.     """Remove ``senderkey`` from connections."""
  513.     _remove_back_refs(senderkey)
  514.     try:
  515.         del connections[senderkey]
  516.     except KeyError:
  517.         pass
  518.     # Senderkey will only be in senders dictionary if sender 
  519.     # could be weakly referenced.
  520.     try:
  521.         del senders[senderkey]
  522.     except:
  523.         pass
  524.  
  525.  
  526. def _remove_back_refs(senderkey):
  527.     """Remove all back-references to this ``senderkey``."""
  528.     try:
  529.         signals = connections[senderkey]
  530.     except KeyError:
  531.         signals = None
  532.     else:
  533.         for signal, receivers in signals.iteritems():
  534.             for receiver in receivers:
  535.                 _kill_back_ref(receiver, senderkey)
  536.  
  537.  
  538. def _remove_old_back_refs(senderkey, signal, receiver, receivers):
  539.     """Kill old ``senders_back`` references from ``receiver``.
  540.  
  541.     This guards against multiple registration of the same receiver for
  542.     a given signal and sender leaking memory as old back reference
  543.     records build up.
  544.  
  545.     Also removes old receiver instance from receivers.
  546.     """
  547.     try:
  548.         index = receivers.index(receiver)
  549.         # need to scan back references here and remove senderkey
  550.     except ValueError:
  551.         return False
  552.     else:
  553.         old_receiver = receivers[index]
  554.         del receivers[index]
  555.         found = 0
  556.         signals = connections.get(signal)
  557.         if signals is not None:
  558.             for sig, recs in connections.get(signal, {}).iteritems():
  559.                 if sig != signal:
  560.                     for rec in recs:
  561.                         if rec is old_receiver:
  562.                             found = 1
  563.                             break
  564.         if not found:
  565.             _kill_back_ref(old_receiver, senderkey)
  566.             return True
  567.         return False
  568.         
  569.         
  570. def _kill_back_ref(receiver, senderkey):
  571.     """Do actual removal of back reference from ``receiver`` to
  572.     ``senderkey``."""
  573.     receiverkey = id(receiver)
  574.     senders = senders_back.get(receiverkey, ())
  575.     while senderkey in senders:
  576.         try:
  577.             senders.remove(senderkey)
  578.         except:
  579.             break
  580.     if not senders:
  581.         try:
  582.             del senders_back[receiverkey]
  583.         except KeyError:
  584.             pass
  585.     return True
  586.  
  587.     
  588.